package com.basker.pisces.orm.database;

import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import com.basker.pisces.concurrent.lock.ILock;
import com.basker.pisces.concurrent.lock.ILockManager;
import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.orm.data.meta.DataSetMeta;
import com.basker.pisces.orm.data.meta.TableMeta;
import com.basker.pisces.orm.session.ISqlSession;
import com.basker.pisces.orm.session.ISqlSessionFactory;
import com.basker.pisces.orm.session.SqlSessionFactoryManager;
import com.basker.pisces.redis.sequence.RedisSequenceGenerator;
import com.basker.pisces.spring.SpringApplicationContext;
import com.basker.pisces.utils.NumberUtils;

/**
 * 获取数据表当前主键的最大值,和redis序列对象比较和同步
 *
 * @author hangwen
 */
class RedisSequenceSynchronizer {

    private DataSetMeta dataSetMeta;
    private RedisSequenceGenerator redisSequenceGenerator;

    public RedisSequenceSynchronizer(DataSetMeta dataSetMeta, RedisSequenceGenerator redisSequenceGenerator) {
        this.dataSetMeta = dataSetMeta;
        this.redisSequenceGenerator = redisSequenceGenerator;
    }

    public void syn() {
        try (ISqlSession session = this.getSqlSession()) {
            for (TableMeta tableMeta : this.dataSetMeta.getTableMetas()) {
                this.syn(tableMeta, session);
            }
        } catch (Exception e) {
            throw ExceptionFactory.createRuntimeException(e);
        }

    }

    private long getMaxIdValue(TableMeta tableMeta, ISqlSession sqlSession) {
        String pkFieldName = tableMeta.getPkFieldMeta().getName();
        String tableName = tableMeta.getName();
        String sql = MessageFormat.format("SELECT MAX({0}) FROM {1}", pkFieldName, tableName);

        List<Map<String, Object>> list = sqlSession.queryForList(sql, ArrayUtils.EMPTY_OBJECT_ARRAY);
        if (list.size() > 0) {
            Object val = list.get(0).values().iterator().next();
            if (val != null) {
                return NumberUtils.parseLong(val);
            }
        }

        return 0L;
    }

    private ISqlSession getSqlSession() {
        ISqlSessionFactory factory = SqlSessionFactoryManager.getDfaultFactory();
        ISqlSession session = null;

        String schema = this.dataSetMeta.getSchema();

        if (StringUtils.isEmpty(schema)) {
            session = factory.createSession();
        } else {
            session = factory.createSession(schema);
        }

        return session;

    }

    private void syn(TableMeta tableMeta, ISqlSession sqlSession) {
        if (tableMeta.isSubTable()) {
            return;
        }

        Class<?> pkValueType = tableMeta.getPkFieldMeta().getDataField().getFieldClass();
        if (!NumberUtils.isNumberClass(pkValueType)) {
            return;
        }

        String tableName = tableMeta.getName();

        ILockManager lockManager = SpringApplicationContext.getInstance().getBean(ILockManager.class);
        ILock lock = lockManager.getLock("seq:syn:" + tableName);

        if (!lock.lock(500, TimeUnit.MILLISECONDS)) {
            return;
        }

        try {
            long maxIdValue = getMaxIdValue(tableMeta, sqlSession);
            long currentValue = this.redisSequenceGenerator.current(tableName);

            if (currentValue < maxIdValue) {
                this.redisSequenceGenerator.set(tableName, maxIdValue);
            }
        } finally {
            lock.unlock();
        }

    }

}
